package common

import (
	"errors"
	"io"
	"reflect"
	"shell/common/packages"
	"shell/global"
	"shell/utils"
	"strings"
)

var (
	ErrNotCloseStream = errors.New("stream not close because peer not accept")
)

/*
	根据quic流读取字节流并序列化为指定的包
*/

// 从流中读取数据，并反序列化为包
// ptr必须是指针
func ReadPackage(re io.Reader, pool *utils.BytesPool, ptr any) error {
	if re == nil || pool == nil || ptr == nil {
		return global.ErrNil
	}
	t := reflect.TypeOf(ptr)
	if t == nil {
		return global.ErrNil
	}
	if t.Kind() != reflect.Pointer {
		return errors.New("error arg type error, need pointer")
	}
	pkg, ok := ptr.(packages.Package)
	if !ok {
		return packages.ErrErrorType
	}
	buff1300 := make([]byte, 1300)
	buff := pool.Get()
	defer pool.Put(buff)

	pkgType := pkg.GetAction()
	var role packages.RctlRole
	first := true
	md := new(packages.PackageMetadata)
	for {
		rn, err := io.ReadFull(re, buff1300[:3])
		if err != nil {
			return err
		}
		if rn != 3 {
			return errors.New("error read length error")
		}
		_, err = md.Unmarshal(buff1300)
		if err != nil {
			return err
		}
		if first {
			role = md.Role
			first = false
		} else {
			if role != md.Role {
				return packages.ErrRctlRoleChange
			}
		}
		if md.PkgAct != pkgType {
			return packages.ErrPkgActChanged
		}
		rn, err = io.ReadFull(re, buff1300[:md.Len])
		if err != nil {
			return err
		}
		if rn != md.Len {
			return errors.New("error read length error")
		}
		buff.Write(buff1300[:rn])
		if !md.HasNext {
			break
		}
	}
	return packages.UnmarshalPackage(buff.Bytes(), ptr, role)
}

// 向一个流写入一个包
func WritePackage(wr io.Writer, ptr any) error {
	if wr == nil || ptr == nil {
		return global.ErrNil
	}
	t := reflect.TypeOf(ptr)
	if t == nil {
		return global.ErrNil
	}
	if t.Kind() != reflect.Pointer {
		return errors.New("error arg type error, need pointer")
	}
	_, ok := ptr.(packages.Package)
	if !ok {
		return packages.ErrErrorType
	}
	b, err := packages.MarshalPackage(ptr)
	if err != nil {
		return err
	}
	for _, v := range b {
		if len(v) < 3 || len(v) > 1300 {
			return packages.ErrSliceLen
		}
	}
	for _, v := range b {
		wn, err := wr.Write(v)
		if err != nil {
			return err
		}
		if wn != len(v) {
			return errors.New("error write length")
		}
	}
	return nil
}

// 探测一个包的类型
func DetectPackage(re io.Reader) (*packages.PackageMetadata, error) {
	info := make([]byte, 3)
	n, e := io.ReadFull(re, info)
	if e != nil {
		return nil, e
	}
	if n != 3 {
		e = errors.New("error read length")
		return nil, e
	}
	pd := new(packages.PackageMetadata)
	_, e = pd.Unmarshal(info)
	return pd, e
}

// 在探测包类型后，接收一个包
func ReadPackageAfterDetect(re io.Reader, pool *utils.BytesPool, in any, metadata *packages.PackageMetadata) error {
	if re == nil || pool == nil || in == nil || metadata == nil {
		return global.ErrNil
	}
	t := reflect.TypeOf(in)
	if t == nil {
		return global.ErrNil
	}
	if t.Kind() != reflect.Pointer {
		return errors.New("error arg type error, need pointer")
	}
	mar, ok := in.(packages.Unmarshalable)
	if !ok {
		return packages.ErrErrorType
	}
	pkg, ok := in.(packages.Package)
	if !ok {
		return packages.ErrErrorType
	}
	buff := pool.Get()
	defer pool.Put(buff)
	buff1300 := make([]byte, 1300)
	role := metadata.Role
	for {
		if pkg.GetAction() != metadata.PkgAct {
			return packages.ErrErrorType
		}
		rn, err := io.ReadFull(re, buff1300[:metadata.Len])
		if err != nil {
			return err
		}
		if rn != metadata.Len {
			return errors.New("error read length")
		}
		buff.Write(buff1300[:rn])

		// 如果有下一个包，则读取元数据
		if !metadata.HasNext {
			break
		}
		rn, err = io.ReadFull(re, buff1300[:3])
		if err != nil {
			return err
		}
		if rn != 3 {
			return errors.New("error read length")
		}
		_, err = metadata.Unmarshal(buff1300)
		if err != nil {
			return err
		}
		if role != metadata.Role {
			return packages.ErrRctlRoleChange
		}
	}
	un, err := mar.Unmarshal(buff.Bytes())
	if err != nil {
		return err
	}
	if un != buff.Len() {
		return errors.New("error unmarshal length")
	}
	pkg.SetRole(role)
	return nil
}

// 列出被控端，参数stream是一个连接到代理端的流，如果发生错误，关闭stream
func ListControlled(stream io.ReadWriteCloser, pool *utils.BytesPool, role packages.RctlRole) ([]*packages.ControlledStat, error) {
	if stream == nil || pool == nil {
		return nil, global.ErrNil
	}
	req := packages.NewControlledReq(role)
	err := WritePackage(stream, req)
	if err != nil {
		return nil, err
	}
	resp := new(packages.ControlledResp)
	err = ReadPackage(stream, pool, resp)
	if err != nil {
		return nil, err
	}
	if req.CmdUUID != resp.CmdUUID {
		stream.Close()
		return nil, packages.ErrCmdUUIDChanged
	}
	return resp.Stats, nil
}

func MatchUUID(stats []*packages.ControlledStat, uuidPrefix string) []*packages.ControlledStat {
	if stats == nil || uuidPrefix == "" {
		return nil
	}
	result := make([]*packages.ControlledStat, 0)
	for _, v := range stats {
		if strings.HasPrefix(v.UUID.String(), uuidPrefix) {
			result = append(result, v)
		}
	}
	return result
}
